#include "datacenter.h"
#include "QFile"
#include "QJsonDocument"
#include "QDir"
#include "QJsonObject"
#include "QStandardPaths"
namespace model
{
DataCenter* DataCenter::instance = nullptr;

DataCenter *DataCenter::getInstance()
{
    if(instance == nullptr)
    {
        instance = new DataCenter();
    }
    return instance;
}

DataCenter::~DataCenter()
{
    // 释放所有的成员
    // 此处不必判定 nullptr, 直接 delete 即可!
    // C++ 标准中明确规定, 针对 nullptr 进行 delete, 是合法行为, 不会有任何副作用.
    delete myself;
    delete friendList;
    delete chatSessionList;
    delete memberList;
    delete applyList;
    delete recentMessages;
    delete unreadMessageCount;
    delete searchUserResult;
    delete searchMessageResult;
}

DataCenter::DataCenter()
    : netClient(this)
{
    // 此处只是把这几个 hash 类型的属性 new 出实例. 其他的 QList 类型的属性, 都暂时不实例化.
    // 主要是为了使用 nullptr 表示 "非法状态"
    // 对于 hash 来说, 不关心整个 QHash 是否是 nullptr, 而是关心, 某个 key 对应的 value 是否存在~~
    // 通过 key 是否存在, 也能表示该值是否有效.
    recentMessages = new QHash<QString, QList<Message>>();
    memberList = new QHash<QString, QList<UserInfo>>();
    unreadMessageCount = new QHash<QString, int>();

    loadDataFile();
}

void DataCenter::initWebsocketConnection()
{
    netClient.initWebsocket();
}

void DataCenter::initDataFile()
{
    // 构造出文件的路径, 使用 appData 存储文件
    QString basePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QString filePath = basePath + "/ChatClient.json";
    LOG() << "filePath=" << filePath;

    QDir dir;
    if (!dir.exists(basePath)) {
        dir.mkpath(basePath);
    }

    // 构造好文件路径之后, 把文件创建出来.
    // 写方式打开, 并且写入初始内容
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        LOG() << "打开文件失败!" << file.errorString();
        return;
    }
    // 打开成功, 写入初始内容.
    QString data = "{\n\n}";
    file.write(data.toUtf8());
    file.close();
}

void DataCenter::saveDataFile()
{
    QString filePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/ChatClient.json";

    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        LOG() << "文件打开失败! " << file.errorString();
        return;
    }

    // 按照 json 格式来写入数据.
    // 这个对象就可以当做 map 一样来使用.
    QJsonObject jsonObj;
    jsonObj["loginSessionId"] = loginSessionId;

    QJsonObject jsonUnread;
    for (auto it = unreadMessageCount->begin(); it != unreadMessageCount->end(); ++it)
    {
        // 注意 Qt 的迭代器使用细节和 STL 略有差别. 此处不是使用 first / second 的方式
        jsonUnread[it.key()] = it.value();
    }
    jsonObj["unread"] = jsonUnread;

    // 把 json 写入文件了 序列化成字符串
    QJsonDocument jsonDoc(jsonObj);
    QString s = jsonDoc.toJson();
    file.write(s.toUtf8());

    // 关闭文件
    file.close();
}
// 加载文件, 是在 DataCenter 被实例化的时候, 调用执行的
void DataCenter::loadDataFile()
{
    // 确保在加载之前, 先针对文件进行初始化操作.
    QString filePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/ChatClient.json";

    // 判定文件是否存在, 不存在则初始化, 并创建出新的空白的 json 文件
    QFileInfo fileInfo(filePath);
    if (!fileInfo.exists()) {
        initDataFile();
    }

    // 读方式打开文件
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        LOG() << "打开文件失败! " << file.errorString();
        return;
    }

    // 读取到文件内容, 解析为 JSON 对象
    QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll());
    if (jsonDoc.isNull()) {
        LOG() << "解析 JSON 文件失败! JSON 文件格式有错误!";
        file.close();
        return;
    }

    QJsonObject jsonObj = jsonDoc.object();
    this->loginSessionId = jsonObj["loginSessionId"].toString();
    LOG() << "loginSessionId=" << this->loginSessionId;

    this->unreadMessageCount->clear();
    QJsonObject jsonUnread = jsonObj["unread"].toObject();
    for (auto it = jsonUnread.begin(); it != jsonUnread.end(); ++it) {
        this->unreadMessageCount->insert(it.key(), it.value().toInt());
    }

    file.close();
}

void DataCenter::clearUnread(const QString &chatSessionId)
{
    (*unreadMessageCount)[chatSessionId] = 0;

    saveDataFile();
}

void DataCenter::addUnread(const QString &chatSessionId)
{
    if (unreadMessageCount->contains(chatSessionId))
    {
        (*unreadMessageCount)[chatSessionId] += 1;
    }
    else
    {
        unreadMessageCount->insert(chatSessionId, 1);
    }
    saveDataFile();
}

int DataCenter::getUnreadCount(const QString &chatSessionId) const
{
    return (*unreadMessageCount)[chatSessionId];
}

void DataCenter::getMyselfAsync()
{
    //DataCenter 只负责处理数据 网络通信需要NetClient
    netClient.getMyself(loginSessionId);
}

void DataCenter::resetMyself(const std::shared_ptr<xchat::GetUserInfoRsp> &resp)
{
    if (myself == nullptr)
    {
        myself = new UserInfo();
    }
    const xchat::UserInfo& userInfo = resp->userInfo();
    myself->load(userInfo);

}

UserInfo *DataCenter::getMyself() const
{
    return myself;
}

QList<UserInfo> *DataCenter::getFriendList() const
{
    return friendList;
}

void DataCenter::getFriendListAsync()
{
    netClient.getFriendList(loginSessionId);
}

void DataCenter::resetFriendList(const std::shared_ptr<xchat::GetFriendListRsp> &resp)
{

    if (friendList != nullptr)
    {
        friendList->clear();
    }
    friendList = new QList<UserInfo>();
    for(auto& fi : resp->friendList())
    {
        UserInfo userInfo;
        userInfo.load(fi);
        friendList->push_back(userInfo);
    };
}

QList<ChatSessionInfo> *DataCenter::getChatSessionList() const
{
    return chatSessionList;
}

void DataCenter::getChatSessionListAsync()
{
    netClient.getChatSessionList(loginSessionId);
}

void DataCenter::resetChatSessionList(const std::shared_ptr<xchat::GetChatSessionListRsp> &resp)
{
    if (chatSessionList != nullptr)
    {
        chatSessionList->clear();
    }
    chatSessionList = new QList<ChatSessionInfo>();
    for(auto& csi : resp->chatSessionInfoList())
    {
        ChatSessionInfo sessionInfo;
        sessionInfo.load(csi);
        chatSessionList->push_back(sessionInfo);
    };
}

QList<UserInfo> *DataCenter::getFriendApplyList() const
{
    return applyList;
}

void DataCenter::getFriendApplyListAsync()
{
    netClient.getFriendApplyList(loginSessionId);
}

void DataCenter::resetFriendApplyList(const std::shared_ptr<xchat::GetPendingFriendEventListRsp> &resp)
{
    if(applyList != nullptr)
    {
        applyList->clear();
    }
    applyList = new QList<UserInfo>();
    for(auto& fi : resp->event())
    {
        UserInfo userInfo;
        userInfo.load(fi.sender());
        applyList->push_back(userInfo);
    };
}

QList<Message>* DataCenter::getRecentMessage(const QString& chatSessionId)
{
    //LOG recentMessages所有key和value
    // LOG() << "recentMessages 所有 chatSessionId 和 对应的消息列表:";
    // for(auto it = recentMessages->begin(); it != recentMessages->end(); ++it)
    // {
    //     LOG() << "chatSessionId=" << it.key() << " 消息数量=" << it.value().size();
    // }

    if(!recentMessages->contains(chatSessionId))
    {
        //LOG() << "没有找到 chatSessionId=" << chatSessionId << "对应的最近消息列表!";
        return nullptr;
    }
    // for(auto & msg : (*recentMessages)[chatSessionId])
    // {
    //     //debug
    //     LOG() << "最近消息列表 messageID=" << msg.messageID
    //           << " chatSessionID=" << msg.chatSessionID
    //           << " messagecontent: " << msg.content.constData()
    //           << " messageType=" << msg.messageType
    //           << " senderID=" << msg.sender.userID;
    // };

    return &(*recentMessages)[chatSessionId];
}

void DataCenter::getRecentMessageAsync(const QString &chatSessionId, bool updateUI)
{
    netClient.getRecentMessage(loginSessionId, chatSessionId, updateUI);
}

void DataCenter::resetRecentMessage(const QString &chatSessionId, const std::shared_ptr<xchat::GetRecentMsgRsp> &resp)
{
    //拿到 chatSessionId对应的消息列表 必须引用
    QList<Message>& messageList = (*recentMessages)[chatSessionId];
    //LOG() << "chatSessionId 清除内容：" << chatSessionId;
    messageList.clear();
    //遍历响应结果列表
    for(auto& mi : resp->msgList())
    {
        Message msg;
        msg.load(mi);
        //debug
        // LOG() << "收到最近消息 messageID=" << msg.messageID
        //       << " chatSessionID=" << msg.chatSessionID
        //       << " messagecontent: " << msg.content.constData()
        //       << " messageType=" << msg.messageType
        //       << " senderID=" << msg.sender.userID;
        messageList.push_back(msg);
    };

}

ChatSessionInfo *DataCenter::findSessionById(const QString &chatSessionId)
{
    //判断是否为空
    if(chatSessionList == nullptr)
    {
        return nullptr;
    }
    for(auto& sessionInfo : *chatSessionList)
    {
        if(sessionInfo.chatSessionID == chatSessionId)
        {
            return &sessionInfo;
        }
    }
    return nullptr;

}

void DataCenter::setCurrentChatSessionId(const QString &chatSessionId)
{
    currentChatSessionId = chatSessionId;
}

const QString &DataCenter::getCurrentChatSessionId() const
{
    return currentChatSessionId;
}

ChatSessionInfo *DataCenter::findSessionByUserId(const QString &userId)
{
    //判断是否为空
    if(chatSessionList == nullptr)
    {
        return nullptr;
    }
    for(auto& sessionInfo : *chatSessionList)
    {
        if(sessionInfo.userID == userId)
        {
            return &sessionInfo;
        }
    }
    return nullptr;
}

void DataCenter::topChatSessionInfo(const ChatSessionInfo &chatSessionInfo)
{
    if (chatSessionList == nullptr) {
        return;
    }

    // 1. 把这个元素从列表中找到
    auto it = chatSessionList->begin();
    for (; it != chatSessionList->end(); ++it) {
        if (it->chatSessionID == chatSessionInfo.chatSessionID) {
            break;
        }
    }

    if (it == chatSessionList->end()) {
        // 上面的循环没有找到匹配的元素, 直接返回. 正常来说, 不会走这个逻辑的.
        return;
    }

    // 2. 把这个元素备份一下, 然后删除 必须备份！！ 因为这个chatSessionInfo就是从这个列表里找出来的 先删除了再添加就搞笑了
    ChatSessionInfo backup = chatSessionInfo;
    chatSessionList->erase(it);

    // 3. 把备份的元素, 插入到头部
    chatSessionList->push_front(backup);

}

void DataCenter::sendTextMessageAsync(const QString &chatSessionId, const QString &content)
{
    //QString只适合文本消息 所以转成字节数组
    netClient.sendMessage(loginSessionId, chatSessionId, Message_Type::TEXT_TYPE, content.toUtf8(), "");
}

void DataCenter::sendImageMessageAsync(const QString &chatSessionId, const QByteArray &imageData)
{
    netClient.sendMessage(loginSessionId, chatSessionId, Message_Type::IMAGE_TYPE, imageData, "");
}

void DataCenter::sendFileMessageAsync(const QString &chatSessionId, const QString &fileName, const QByteArray &fileData)
{
    netClient.sendMessage(loginSessionId, chatSessionId, Message_Type::FILE_TYPE, fileData, fileName);
}

void DataCenter::sendSpeechMessageAsync(const QString &chatSessionId, const QByteArray &speechData)
{
    netClient.sendMessage(loginSessionId, chatSessionId, Message_Type::SPEECH_TYPE, speechData, "");
}

void DataCenter::speechToTextAsync(const QString &fileId, const QByteArray &speechData)
{
    netClient.speechToText(loginSessionId, fileId, speechData);
}

void DataCenter::addMessage(const Message &message)
{
    if(!recentMessages->contains(message.chatSessionID))
    {
        //如果没有这个会话的消息列表 则创建一个空列表
        (*recentMessages)[message.chatSessionID] = QList<Message>();
    }
    //添加消息到对应会话的消息列表中
    (*recentMessages)[message.chatSessionID].push_back(message);
}

void DataCenter::changeNicknameAsync(const QString &newNickname)
{
    netClient.changeNickname(loginSessionId, newNickname);
}

void DataCenter::resetNickname(const QString &newNickname)
{
    if(myself != nullptr)
    {
        myself->nickname = newNickname;
    }
}

void DataCenter::changeDescriptionAsync(const QString &newDescription)
{
    netClient.changeDescription(loginSessionId, newDescription);
}

void DataCenter::resetDescription(const QString &newDescription)
{
    if(myself != nullptr)
    {
        myself->description = newDescription;
    }
}

void DataCenter::getVerifyCodeAsync(const QString &phoneNumber)
{
    //不需要网关鉴权 不需要loginSessionId
    netClient.getVerifyCode(phoneNumber);
}

void DataCenter::resetVerifyCodeId(const QString &verifyCodeId)
{
    currentVerifyCodeId = verifyCodeId;
}

const QString &DataCenter::getCurrentVerifyCodeId() const
{
    return currentVerifyCodeId;
}

void DataCenter::changePhoneNumberAsync(const QString& newPhoneNumber, const QString& verifyCodeId, const QString& userInputCode)
{
    netClient.changePhoneNumber(loginSessionId, newPhoneNumber, verifyCodeId, userInputCode);
}

void DataCenter::resetPhoneNumber(const QString &newPhoneNumber)
{
    if(myself != nullptr)
    {
        myself->phone = newPhoneNumber;
    }
}

void DataCenter::changeAvatarAsync(const QByteArray &newAvatarData)
{
    netClient.changeAvatar(loginSessionId, newAvatarData);
}

void DataCenter::resetAvatar(const QByteArray &newAvatarData)
{
    if(myself != nullptr)
    {
        myself->avatar = makeIcon(newAvatarData);
    }
}

UserInfo *DataCenter::findFriendById(const QString &userId)
{
    if(friendList == nullptr)
    {
        return nullptr;
    }
    for(auto& friendInfo : *friendList)
    {
        if(friendInfo.userID == userId)
        {
            return &friendInfo;
        }
    }
    return nullptr;
}

void DataCenter::deleteFriendAsync(const QString &userId)
{
    netClient.deleteFriend(loginSessionId, userId);
}

void DataCenter::removeFriend(const QString &userId)
{
    if(friendList == nullptr)
    {
        return;
    }
    //遍历好友列表 找到对应好友并删除
    for(auto it = friendList->begin(); it != friendList->end(); ++it)
    {
        if(it->userID == userId)
        {
            friendList->erase(it);
            break;
        }
    }
    //删除对应会话
    if(chatSessionList != nullptr)
    {
        for(auto it = chatSessionList->begin(); it != chatSessionList->end(); ++it)
        {
            if(it->userID == "")
            {
                //群聊不受影响
                continue;
            }
            if(it->userID == userId)
            {
                //如果当前会话是正在聊天的会话 就清空内容和标题
                if(currentChatSessionId == it->chatSessionID)
                {
                    emit clearCurrentChatSession();
                }
                chatSessionList->erase(it);
                break;
            }
        }
    }
}

void DataCenter::addFriendApplyAsync(const QString &userId)
{
    netClient.addFriendApply(loginSessionId, userId);
}

void DataCenter::acceptFriendApplyAsync(const QString &userId)
{
    netClient.acceptFriendApply(loginSessionId, userId);
}

UserInfo DataCenter::removeFriendApply(const QString &userId)
{
    if(applyList == nullptr)
    {
        return UserInfo();
    }
    UserInfo removedApply;
    //遍历好友申请列表 找到对应好友申请并删除
    for(auto it = applyList->begin(); it != applyList->end(); ++it)
    {
        if(it->userID == userId)
        {
            removedApply = *it;
            applyList->erase(it);
            return removedApply;
        }
    }
    return UserInfo();
}

void DataCenter::rejectFriendApplyAsync(const QString &userId)
{
    netClient.rejectFriendApply(loginSessionId, userId);
}

void DataCenter::createGroupChatSessionAsync(const QList<QString> &memberUserIds)
{
    netClient.createGroupChatSession(loginSessionId, memberUserIds);
}

void DataCenter::getMemberList(const QString &chatSessionId)
{
    netClient.getMemberList(loginSessionId, chatSessionId);
}

void DataCenter::resetChatSessionMemberList(const QString &chatSessionId, const QList<xchat::UserInfo> &memberInfos)
{
    QList<UserInfo>& members = (*memberList)[chatSessionId];
    members.clear();
    for(const auto& mi : memberInfos)
    {
        UserInfo userInfo;
        userInfo.load(mi);
        members.push_back(userInfo);
    }
}

QList<UserInfo> *DataCenter::getChatSessionMemberList(const QString &chatSessionId)
{
    if(!memberList->contains(chatSessionId))
    {
        return nullptr;
    }
    return &(*memberList)[chatSessionId];
}

void DataCenter::searchUserAsync(const QString &keyword)
{
    netClient.searchUser(loginSessionId, keyword);
}

void DataCenter::resetSearchUserList(const QList<xchat::UserInfo> &searchInfos)
{
    if(searchUserResult != nullptr)
    {
        searchUserResult->clear();
    }
    searchUserResult = new QList<UserInfo>();
    for(const auto& si : searchInfos)
    {
        UserInfo userInfo;
        userInfo.load(si);
        searchUserResult->push_back(userInfo);
    };
}

QList<UserInfo> *DataCenter::getSearchUserResult()
{
    return searchUserResult;
}

void DataCenter::searchHistoryMessageAsync(const QString &keyword)
{
    netClient.searchHistoryMessage(loginSessionId, currentChatSessionId, keyword);
}

void DataCenter::resetSearchMessageList(const QList<xchat::MessageInfo> &messageInfos)
{
    if(searchMessageResult != nullptr)
    {
        searchMessageResult->clear();
    }
    searchMessageResult = new QList<Message>();
    for(const auto& mi : messageInfos)
    {
        Message msg;
        msg.load(mi);
        searchMessageResult->push_back(msg);
    };
}

QList<Message> *DataCenter::getSearchMessageResult()
{
    return searchMessageResult;
}

void DataCenter::searchHistoryMessageByTimeAsync(const QDateTime &begTime, const QDateTime &endTime)
{
    netClient.searchHistoryMessageByTime(loginSessionId, currentChatSessionId, begTime, endTime);
}

void DataCenter::userLoginAsync(const QString &userName, const QString &password)
{
    netClient.userLogin(userName, password);
}

void DataCenter::resetLoginSessionId(const QString &sessionId)
{
    loginSessionId = sessionId;

    //一旦登录态改变 就保存到文件
    saveDataFile();
}

void DataCenter::userRegisterAsync(const QString &userName, const QString &password)
{
    netClient.userRegister(userName, password);
}

void DataCenter::phoneLoginAsync(const QString &phoneNumber, const QString &verifyCode)
{
    netClient.phoneLogin(phoneNumber, this->getCurrentVerifyCodeId(), verifyCode);
}

void DataCenter::phoneRegisterAsync(const QString &phoneNumber, const QString &verifyCode)
{
    netClient.phoneRegister(phoneNumber, this->getCurrentVerifyCodeId(), verifyCode);
}

void DataCenter::getSingleFileAsync(const QString &fileId)
{
    netClient.getSingleFile(loginSessionId, fileId);
}




}
